﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using Phoenix.Xna.Framework.Properties;

namespace Phoenix.Xna.Framework.Collections
{
    public abstract class GenericCollection<T> : IList, ICollection, INotifyCollectionChanged,
        INotifyPropertyChanged, IList<T>, ICollection<T>, IEnumerable<T>, ISortableCollection<T>
    {
        protected List<T> InnerList { get; set; }

        public GenericCollection()
        {
            InnerList = new List<T>();
        }

        protected virtual void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler handler = CollectionChanged;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void NotifyPropertyChanged(string property)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(property));
            }
        }

        #region IList<T> Members

        public virtual int IndexOf(T item)
        {
            if (!(item is DBNull) && item != null && !InnerList.Contains(item))
            {
                return InnerList.IndexOf(item);
            }

            return -1;
        }

        public virtual void Insert(int index, T item)
        {
            InnerList.Insert(index, item);
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
            NotifyPropertyChanged("Count");
        }

        public virtual void RemoveAt(int index)
        {
            if (index > 0 && index < InnerList.Count)
            {
                T item = InnerList[index];
                InnerList.RemoveAt(index);
                NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
                NotifyPropertyChanged("Count");
            }
            else
            {
                throw new ArgumentOutOfRangeException(string.Format(Resources.ArgumentOutOfRangeException, "index", "0", InnerList.Count.ToString()));
            }
        }

        public virtual T this[int index]
        {
            get
            {
                if (index > 0 && index < InnerList.Count)
                {
                    return InnerList[index];
                }
                else
                {
                    throw new ArgumentOutOfRangeException(string.Format(Resources.ArgumentOutOfRangeException, "index", "0", InnerList.Count.ToString()));
                }
            }

            set
            {
                if (index > 0 && index < InnerList.Count)
                {
                    T oldItem = InnerList[index];
                    InnerList[index] = value;
                    NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldItem, value));
                }
                else
                {
                    throw new ArgumentOutOfRangeException(string.Format(Resources.ArgumentOutOfRangeException, "index", "0", InnerList.Count.ToString()));
                }
            }
        }

        #endregion

        #region ICollection<T> Members

        public virtual void Clear()
        {
            List<T> copyItems = new List<T>();

            for (int i = 0; i < InnerList.Count; i++)
            {
                copyItems.Add(InnerList[i]);
            }

            InnerList.Clear();
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, copyItems));
            NotifyPropertyChanged("Count");
        }

        public virtual bool Contains(T item)
        {
            return InnerList.Contains(item);
        }

        public virtual void CopyTo(T[] array, int index)
        {
            InnerList.CopyTo(array, index);
        }

        public virtual int Count
        {
            get { return InnerList.Count; }
        }

        public virtual bool IsReadOnly
        {
            get { return false; }
        }

        public virtual bool Remove(T item)
        {
            if (InnerList.Contains(item))
            {
                int index = InnerList.IndexOf(item);
                InnerList.Remove(item);

                if (index > -1)
                {
                    NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
                }
                else
                {
                    NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove));
                }

                NotifyPropertyChanged("Count");
                return true;
            }
            else
            {
                return false;
            }
        }

        public virtual void Add(T item)
        {
            InnerList.Add(item);
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
            NotifyPropertyChanged("Count");
        }

        void ICollection<T>.Add(T item)
        {
            InnerList.Add(item);
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
            NotifyPropertyChanged("Count");
        }

        void ICollection<T>.Clear()
        {
            List<T> copyItems = new List<T>();

            for (int i = 0; i < InnerList.Count; i++)
            {
                copyItems.Add(InnerList[i]);
            }

            InnerList.Clear();
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, copyItems));
            NotifyPropertyChanged("Count");
        }

        bool ICollection<T>.Contains(T item)
        {
            return InnerList.Contains(item);
        }

        void ICollection<T>.CopyTo(T[] array, int index)
        {
            InnerList.CopyTo(array, index);
        }

        int ICollection<T>.Count
        {
            get { return InnerList.Count; }
        }

        bool ICollection<T>.IsReadOnly
        {
            get { return false; }
        }

        bool ICollection<T>.Remove(T item)
        {
            if (InnerList.Contains(item))
            {
                InnerList.Remove(item);
                NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
                NotifyPropertyChanged("Count");
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IList Members

        public virtual int Add(object item)
        {
            Add((T)item);
            return 1;
        }

        public virtual bool Contains(object item)
        {
            return Contains((T)item);
        }

        public virtual int IndexOf(object item)
        {
            return IndexOf((T)item);
        }

        public virtual void Insert(int index, object item)
        {
            Insert(index, (T)item);
        }

        public virtual bool IsFixedSize
        {
            get { return (InnerList as IList).IsFixedSize; }
        }

        public virtual void Remove(object item)
        {
            Remove((T)item);
        }

        object IList.this[int index]
        {
            get
            {
                return this[index];
            }

            set
            {
                this[index] = (T)value;
            }
        }

        #endregion

        #region ICollection Members

        public void CopyTo(Array array, int index)
        {
            (InnerList as IList).CopyTo(array, index);
        }

        public bool IsSynchronized
        {
            get { return (InnerList as IList).IsSynchronized; }
        }

        public object SyncRoot
        {
            get { return (InnerList as IList).SyncRoot; }
        }

        #endregion

        #region ISortableCollection Members

        public virtual void Sort()
        {
            InnerList.Sort();
        }

        public virtual void Sort(Comparison<T> comparision)
        {
            InnerList.Sort(comparision);
        }

        public virtual void Sort(IComparer<T> comparer)
        {
            InnerList.Sort(comparer);
        }

        public void Sort(int index, int count, IComparer<T> comparer)
        {
            InnerList.Sort(index, count, comparer);
        }

        #endregion
    }
}
